goog.provide('mp.ui.GLayout');

goog.require('goog.events.MouseWheelHandler');
goog.require('goog.ui.Component.EventType');
goog.require('goog.ui.Container');
goog.require('goog.ui.registry');
goog.require('mp.graphics.Graphics');
goog.require('mp.ui.GLayoutRenderer');
goog.require('goog.fx.Animation');
goog.require('goog.fx');

mp.ui.GLayout = function(graphics) {
  goog.base(this, null,
      mp.ui.GLayoutRenderer.getInstance(),
      graphics.getImplementation().getDomHelper());

  this.graphics_ = graphics;
  this.setElementInternal(graphics.mainLayer_.getWrapper().getElement());

  goog.dom.setProperties(graphics.bgLayer_.getWrapper().getElement(), {'id': 'mp-bg-layer'});
  goog.dom.setProperties(graphics.placeholderLayer_.getWrapper().getElement(), {'id': 'mp-placeholder-layer'});
  goog.dom.setProperties(graphics.mainLayer_.getWrapper().getElement(), {'id': 'mp-main-layer'});
  goog.dom.setProperties(graphics.captionLayer_.getWrapper().getElement(), {'id': 'mp-caption-layer'});
  goog.dom.setProperties(graphics.toolsLayer_.getWrapper().getElement(), {'id': 'mp-tools-layer'});
};
goog.inherits(mp.ui.GLayout, goog.ui.Container);

mp.ui.GLayout.prototype.graphics_;


/**
 * Event handler.
 * @type {goog.events.MouseWheelHandler}
 * @private
 */
mp.ui.GLayout.prototype.googMouseWheelHandler_;


mp.ui.GLayout.prototype.selectedItem_ = null;

mp.ui.GLayout.prototype.animQueue_ = null;

mp.ui.GLayout.prototype.wrapper_ = null;

mp.ui.GLayout.prototype.getGraphics = function() {
  return this.graphics_;
};

/**
 * @override
 */
mp.ui.GLayout.prototype.enterDocument = function() {
  goog.base(this, 'enterDocument');

  this.listenToPaneEvents_(this.getGraphics().getSvgElement());
};

/** @override */
goog.ui.Control.prototype.exitDocument = function() {
  goog.base(this, 'exitDocument');

  if (this.googMouseWheelHandler_) {
    goog.events.unlisten(this.googMouseWheelHandler_, 
        goog.events.MouseWheelHandler.EventType.MOUSEWHEEL, this.handleMouseWheel);
  }
}

/** @override */
mp.ui.GLayout.prototype.disposeInternal = function() {
  goog.base(this, 'disposeInternal');

  if (this.googMouseWheelHandler_) {
    this.googMouseWheelHandler_.dispose();
    delete this.googMouseWheelHandler_;
  }
  this.selectedItem_ = null;
};


/**
 * Returns the event handler for this component, lazily created the first time
 * this method is called.
 * @return {!goog.events.MouseWheelHandler} Mouse wheel event handler for this component.
 * @protected
 */
mp.ui.GLayout.prototype.getMouseWheelHandler = function(element) {
  var element = !! element ? element : this.getElement ? this.getElement() : this;
  return this.googMouseWheelHandler_ ||
      (this.googMouseWheelHandler_ = new goog.events.MouseWheelHandler(element));
};


/**
 * Subscribes to events dispatched by items.
 * @private
 */
mp.ui.GLayout.prototype.listenToPaneEvents_ = function(element) {
  // Listen for SELECT, UNSELECT, DISABLE, and HIDE events dispatched by items.
  var element = !!element ? element : this.getElement();

  this.getHandler().
      listen(element, goog.events.EventType.MOUSEDOWN, this.handleMouseDown).
      listen(this, goog.ui.Component.EventType.DISABLE, this.handleTabDisable).
      listen(this, goog.ui.Component.EventType.SELECT, this.handleItemSelect).
      listen(this, goog.ui.Component.EventType.UNSELECT, this.handleItemUnselect).
      listen(this.getMouseWheelHandler(element), goog.events.MouseWheelHandler.EventType.MOUSEWHEEL, this.handleMouseWheel);
};

mp.ui.GLayout.prototype.removeChild = function(item, opt_unrender) {
  // This actually only accepts goog.ui.Controls. There's a TODO
  // on the superclass method to fix this.
  this.deselectIfSelected(/** @type {goog.ui.Control} */ (item));
  return goog.base(this, 'removeChild', item, opt_unrender);
};


/**
 * @return {goog.ui.Control?} The currently selected item (null if none).
 */
mp.ui.GLayout.prototype.getSelectedItem = function() {
  return this.selectedItem_;
};

mp.ui.GLayout.prototype.setSelectedItem = function(item) {
  if (item) {
    // Select the item and have it dispatch a SELECT event, to be handled in
    // handleItemSelect() below.
    item.setSelected(true);
  } else if (this.getSelectedItem()) {
    // De-select the currently selected item and have it dispatch an UNSELECT
    // event, to be handled in handleItemUnselect() below.
    this.getSelectedItem().setSelected(false);
  }
};

mp.ui.GLayout.prototype.deselectIfSelected = function(item) {
  if (item && item == this.getSelectedItem()) {
    this.setSelectedItem(null);
  }
};

mp.ui.GLayout.prototype.handleItemSelect = function(e) {
  if (this.selectedItem_ && this.selectedItem_ != e.target) {
    // Deselect currently selected item.
    this.selectedItem_.setSelected(false);
  }
  this.selectedItem_ = /** @type {mp.ui.GLayoutElm} */ (e.target);
};

mp.ui.GLayout.prototype.handleItemUnselect = function(e) {
  if (e.target == this.selectedItem_) {
    this.selectedItem_ = null;
  }
};

mp.ui.GLayout.prototype.handleItemDisable = function(e) {
  this.deselectIfSelected(/** @type {mp.ui.GLayoutElm} */ (e.target));
};

mp.ui.GLayout.prototype.handleMouseDown = function(e) {
  goog.base(this, 'handleMouseDown', e);


  var drgelm = this.getGraphics().getParentElement();
  var control = this.getOwnerControl(/** @type {Node} */ (e.target));
  if(e.target == this.getGraphics().getSvgElement()) {
  //if(!control || 0 && control != this.getSelectedItem()) {
    var dragger = new goog.fx.Dragger(drgelm);
    dragger.graphics = this.getGraphics();

    dragger.addEventListener(goog.fx.Dragger.EventType.START, function(e) {
      e.dragger.origin = e.dragger.graphics.getCoordOrigin();
    });

    dragger.defaultAction = function(x, y) {
      var pos = goog.style.getPageOffset(this.target);
      var scale = this.graphics.getClientScale();
      this.graphics.setCoordOrigin(
          this.origin.x - (x-pos.x)/scale,
          this.origin.y - (y-pos.y)/scale);
    };
  } else if(!!control){
    
    if(control.isEnabled())
      this.graphics_.bringToFront(control.getGElement());
    
    //control.getGElement().setRotation(10);

    var dragger = new goog.fx.Dragger(drgelm);
    dragger.mpObject = control;

    dragger.addEventListener(goog.fx.Dragger.EventType.START, function(e) {
      e.dragger.mpPosition = e.dragger.mpObject.getGElement().getPosition();
    });

    dragger.defaultAction = function(x, y) {
      var pos = goog.style.getPageOffset(this.target);
      var scale = this.mpObject.getGElement().graphics_.getClientScale();
      this.mpObject.getGElement().setPosition(
          this.mpPosition.x + (x-pos.x)/scale, 
          this.mpPosition.y + (y-pos.y)/scale);
     };
  }

  if(!!dragger) {
    dragger.addEventListener(goog.fx.Dragger.EventType.END, function(e) {
      e.dragger.dispose();
    });

    dragger.startDrag(e);
  }
};

/**
 * Handles {@code MOUSEWHEEL}.
 * @param {goog.events.MouseWheelEvent} e MouseWheel event to handle.
 * @protected
 */
mp.ui.GLayout.prototype.handleMouseWheel = function(e) {
  var delta = (e.detail>0 ? -1 : 1) * 0.5;
  var scale = this.getGraphics().getClientScale();  
  var newScale = scale + scale*delta;
  
  e.preventDefault();

  if(newScale<=0.1)
    return;
  
  var trgt = this.getGraphics().getParentElement();
  var pos = goog.style.getClientPosition(trgt);

  if(!!this.animQueue_ && !this.animQueue_.isStopped()) {
    this.animQueue_.stop();
    this.animQueue_.destroy();
  }

  var anim = this.animQueue_ = new goog.fx.Animation(
        [scale, e.clientX-pos.x, e.clientY-pos.y],
        [newScale, e.clientX-pos.x, e.clientY-pos.y], 
        500, goog.fx.easing.easeOut);
  
  this.getHandler().
      listen(anim, goog.fx.Animation.EventType.ANIMATE, function(e) {
        var scale = this.getGraphics().getClientScale();  
        var origin = this.getGraphics().getCoordOrigin();
        var delta = e.coords[0] - scale;
        var m = delta/(scale*e.coords[0]);

        if(e.coords[0]>0.1) {
          this.getGraphics().setClientScale(e.coords[0]);
          this.getGraphics().setCoordOrigin(
              origin.x + e.coords[1] * m, 
              origin.y + e.coords[2] * m);
        }
      }).
      listen(anim, goog.fx.Animation.EventType.END, function(e) {
        e.anim.destroy();
      });
  
  anim.play();
};

/** @override */
mp.ui.GLayout.prototype.render = function(parentElement) {
  this.wrapper_ = parentElement;
  this.getGraphics().render(parentElement);
  goog.base(this, 'render', this.getGraphics().getWrapper().getElement());
};

/** @override */
mp.ui.GLayout.prototype.render_ = function(opt_parentElement,
                                               opt_beforeNode) {
  // If this component has a parent component that isn't in the document yet,
  // we don't call enterDocument() here.  Instead, when the parent component
  // enters the document, the enterDocument() call will propagate to its
  // children, including this one.  If the component doesn't have a parent
  // or if the parent is already in the document, we call enterDocument().
  if (!this.parent_ || this.parent_.isInDocument()) {
    this.enterDocument();
  }
};

// Register a decorator factory function for mp.ui.GLayouts.
goog.ui.registry.setDecoratorByClassName(mp.ui.GLayoutRenderer.CSS_CLASS,
    function() {
      return new mp.ui.GLayout(null);
    });
